package validate

import (
	"encoding/json"
	"gitverse.ru/synapse/kubelatte/pkg/logs"
	"gitverse.ru/synapse/kubelatte/pkg/patchs"
	"reflect"
	"strconv"
	"strings"

	"github.com/wI2L/jsondiff"
)

type ConvPatch2JSON struct {
	Handlers map[string]func(operation jsondiff.Operation) map[string]interface{}
}

func GetConvertor() ConvPatch2JSON {
	conv := ConvPatch2JSON{Handlers: map[string]func(operation jsondiff.Operation) map[string]interface{}{
		"add": func(operation jsondiff.Operation) map[string]interface{} {
			return Convert2JSONMainFunc("add", operation, operation.Value)
		},

		"replace": func(operation jsondiff.Operation) map[string]interface{} {
			return Convert2JSONMainFunc("replace", operation, operation.Value)
		},

		"remove": func(operation jsondiff.Operation) map[string]interface{} {
			return Convert2JSONMainFunc("remove", operation, operation.OldValue)
		},
	}}

	return conv
}

func Convert2JSONMainFunc(currCase string, operation jsondiff.Operation,
	operationValue interface{}) map[string]interface{} {

	if !reflect.ValueOf(operationValue).IsValid() {
		return nil
	}

	es := strings.Split(operation.Path, "/")
	var m = map[string]interface{}{}

	last := len(es) - 1
	var current interface{}

	for i := last; i > 0; i-- {
		if i != 0 && es[0] == "" {
			val, err := strconv.Atoi(es[i])
			if currCase == "add" {
				if es[i] == "-" {
					current = IthCaseHandle(current, i, val, last, operationValue)
					continue
				}
			}

			if i == last {
				current = LastIndexHandle(es[i], current, operationValue)
				continue
			}

			if err == nil {
				current = NilErrHandle(i, current, last, operationValue)
				continue
			}

			m, current = FirstIndexHandle(es[i], current, i, m)
		}

		continue
	}

	return m
}

func NilErrHandle(i int, val interface{}, last int, operationValue interface{}) interface{} {
	var current interface{}
	var c []interface{}

	if val != nil {
		c = append(c, val)
	}

	if i == last {
		current = operationValue
	}

	if current != nil {
		current = append(c, current)
	} else {
		current = c
	}

	return current
}

func IthCaseHandle(current interface{}, i int, val int, last int, operationValue interface{}) interface{} {
	c := make([]interface{}, val)
	if i == last {
		current = operationValue
	}
	current = append(c, current)

	return current
}

func LastIndexHandle(element string, current interface{}, operationValue interface{}) interface{} {
	c := map[string]interface{}{}
	c[element] = operationValue
	current = c
	return current
}

func FirstIndexHandle(element string, current interface{},
	i int, m map[string]interface{}) (map[string]interface{}, interface{}) {
	c := map[string]interface{}{}
	c[element] = current
	if i == 1 {
		m = c
	}
	current = c

	return m, current
}

func (conv *ConvPatch2JSON) Convert(operation jsondiff.Operation) map[string]interface{} {
	return conv.Handlers[operation.Type](operation)
}

func (conv *ConvPatch2JSON) ConvertJSON(operation jsondiff.Operation) []byte {
	result, err := json.Marshal(conv.Handlers[operation.Type](operation))
	if err != nil {
		logs.Error(err)
	}
	return result
}

func ConvertAddToReplace(add jsondiff.Operation) []jsondiff.Operation {
	return CreateReplacePatch(strings.ReplaceAll(add.Path, "-", "0"), add.Value)
}

func ReplacePatchReflectCasesHandle(element interface{},
	patches []jsondiff.Operation, path string) (bool, []jsondiff.Operation) {
	add := false
	if len(element.([]interface{})) == 0 {
		add = true
	}
	for s, i := range element.([]interface{}) {
		patches = append(patches, CreateReplacePatch(path+"/"+strconv.Itoa(s), i)...)
	}

	return add, patches
}

func CreateReplacePatch(path string, element interface{}) []jsondiff.Operation {
	var patches []jsondiff.Operation
	add := false
	switch reflect.ValueOf(element).Kind() {
	case reflect.Map:
		if len(element.(map[string]interface{})) == 0 {
			add = true
		}
		for s, i := range element.(map[string]interface{}) {
			patches = append(patches, CreateReplacePatch(path+"/"+s, i)...)
		}
		break
	case reflect.Array:
		add, patches = ReplacePatchReflectCasesHandle(element, patches, path)
		break
	case reflect.Struct:
		//по идее сюда мы попадать не должны
		logs.Error("Struct values are not supported")
		break
	case reflect.Slice:
		add, patches = ReplacePatchReflectCasesHandle(element, patches, path)
		break
	default:
		add = true
	}

	if add {
		replace := patchs.Operation{
			Op:       "replace",
			From:     "",
			Path:     path,
			OldValue: nil,
			Value:    element,
		}
		convToByte, _ := json.Marshal(replace)
		patch := jsondiff.Operation{}
		err := json.Unmarshal(convToByte, &patch)
		if err != nil {
			logs.Errorf("Patch conversion error %v\n", err)
			return nil
		}
		patches = append(patches, patch)
	}

	return patches
}
